#pragma once 
#include <functional>

#include <pthread.h>
#include <unistd.h>
#include "blockQueue.hpp"
#include "doubleBuf.hpp"
#include "circularQueue.hpp"
#include "thread.hpp"

using std::endl;
using std::cout;
// 缓冲区本身需要维护线程安全
// 不同的缓冲区模型
enum class BUFF_TYPE
{
    BLOCK,
    CIRCULAR,
    DOUBLE
};
template<typename T>
class ProConModel
{
public:
    // 具体的生产线程，以及消费线程要做的事情
    
    using excuteCB = std::function<void(T&)>; // 处理任务的回调
    using getCB = std::function<void(T*)>; // 获取任务的回调
    

    ProConModel(BUFF_TYPE mdt = BUFF_TYPE:: BLOCK)
    :_mdt(mdt)
    {
        if(_mdt == BUFF_TYPE::BLOCK)
            _buf = std::make_shared<BlockQueue<T>>();
        else if(_mdt == BUFF_TYPE:: CIRCULAR)
            _buf = std::make_shared<CircularQueue<T>>();
        else if(_mdt == BUFF_TYPE:: DOUBLE)
            _buf = std::shared_ptr<DoubleBuf<T>> (new DoubleBuf<T>);
    }
    // excuteCB _excuteTask;
    // getCB _getTask;
    ProConModel(excuteCB exc, getCB get, BUFF_TYPE mdt = BUFF_TYPE:: BLOCK)
    :_mdt(mdt)
    ,_excuteTask(exc)
    ,_getTask(get)
    {
        if(_mdt == BUFF_TYPE::BLOCK)
            _buf = std::make_shared<BlockQueue<T>>();
        else if(_mdt == BUFF_TYPE:: CIRCULAR)
            _buf = std::make_shared<CircularQueue<T>>();
        else if(_mdt == BUFF_TYPE:: DOUBLE)
            _buf = std::shared_ptr<DoubleBuf<T>> (new DoubleBuf<T>);
    }


    ~ProConModel()
    {

    }

    void consumer()
    {
        while(true)
        {
            T val;
            _buf->pop(&val);
            cout << "消费者线程获取到数据" << val << endl;
            // 进行回调
            // 执行任务
            // void (T&)
            // _excuteTask(val); // 执行任务
        }
    }
    void producer()
    {
        while(true)
        {
            // 进行回调
            // 产生任务
            // T task;
            // _getTask(&task);
            // _buf->push(task);
            int val = rand() % 10;
            _buf->push(val);
            cout << "生产者生产了一个数据" << val << endl;

        }
    }


    // 未能找到bug
    // 在消费者和生产者之间去维护 同步互斥关系，会死锁 --》 转到缓冲区中设置同步互斥关系可以
    // 加锁应该加在最小的粒度上以便于可以达到更高的并发！！！



    // 消费者
    void consumer_err()
    {
        while(true)
        {
            // // pthread_mutex_lock(&_mutex_con);
            // {
            //     pthread_mutex_lock(&_mutex_pro_con);
            //     {
            //         // 没有数据时，消费者在_cond_con条件变量下 阻塞等待
            //         while(_buf->empty())
            //         {
            //             cout << "没有数据，消费者在条件变量下等待" << endl;
            //             sleep(1);
            //             pthread_cond_wait(&_cond_con,&_mutex_pro_con);
            //         }
            //         // 应该是在这里进行一个回调，对数据的处理进行一个回调
            //         T val;
            //         _buf->pop(&val);
            //         //进行数据处理 -- 可进行回调，交给用户进行数据处理
            //         cout << "消费者线程获取到数据" << val << endl;
            //         // 消费一个数据唤醒 在_cond_pro条件下休眠的生产者
            //         pthread_cond_broadcast(&_cond_pro);
            //         // cout << "唤醒生产者" << endl;
            //     }
            //     pthread_mutex_unlock(&_mutex_pro_con);
            // }
            // // pthread_mutex_unlock(&_mutex_con);
        
        }
    }
    // 生产者
    void producer_err()
    {
        while(true)
        {
            // // pthread_mutex_lock(&_mutex_pro);
            // {
            //     pthread_mutex_lock(&_mutex_pro_con);
            //     {
            //         while(_buf->full())
            //         {
            //             cout << "缓冲区满了，生产者在条件变量下等待" << endl;
            //             pthread_cond_wait(&_cond_pro,&_mutex_pro_con);
            //         }
            //         //回调函数：产生数据
            //         int val = rand() % 10;
            //         _buf->push(val);
            //         cout << "生产者生产了一个数据" << val << endl;
            //         pthread_cond_broadcast(&_cond_con);
            //         // cout << "唤醒消费者" <<  endl;
            //     }
            //     pthread_mutex_unlock(&_mutex_pro_con);
            // }
            // // pthread_mutex_unlock(&_mutex_pro);
        }
    }

private:
    BUFF_TYPE _mdt;
    // 指名模板类中的类型时需要typename
    typename basic_buf<T>:: ptr _buf;
    excuteCB _excuteTask;
    getCB _getTask;
};